package com.bobo.base.config;

import com.bobo.base.auth.JWTAuthFilter;
import jakarta.annotation.Resource;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpMethod;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.authentication.configuration.AuthenticationConfiguration;
import org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.AuthenticationEntryPoint;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.access.AccessDeniedHandler;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;


/**
 * SecurityConfig
 *
 * @author LILIBO
 * @since 2023-02-23
 */
@EnableWebSecurity
@EnableMethodSecurity
@Configuration
public class SecurityConfig {

    /**
     * 允许匿名访问的接口 "/**" 表示允许所有
     */
    private static final String ALLOW_ALL = "/**"; // 所有请求

    /**
     * 允许匿名访问的接口列表 [登录接口, 验证接口, 其他...]
     */
    private static final String[] ALLOW_MATCHERS = {"/auth/login", "/auth/verify", "/swagger-ui/**", "/v3/api-docs/**", "/swagger-resources/**", "/webjars/**", "/druid/**"};

    /**
     * 认证用户需要拥有的权限 ["api"]（匹配UserDetails接口的实现类中getAuthorities方法，通常在UserDetailsService接口实现类中设置）
     */
    private static final String[] AUTHORITIES = {"api"};

    @Resource
    private JWTAuthFilter jwtAuthFilter;

    @Resource
    private AuthenticationEntryPoint authenticationEntryPoint;

    @Resource
    private AccessDeniedHandler accessDeniedHandler;

    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }

    /**
     * 配置安全过滤规则
     */
    @Bean
    public SecurityFilterChain filterChain(HttpSecurity httpSecurity) throws Exception {
        // 禁用默认的登录和退出
        httpSecurity.formLogin(AbstractHttpConfigurer :: disable);
        httpSecurity.logout(AbstractHttpConfigurer :: disable);
        // 前后端分离不需要csrf保护
        httpSecurity.csrf(AbstractHttpConfigurer :: disable);
        // 前后端分离不需要Session
        httpSecurity.sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS));
        // 配置请求拦截规则
        httpSecurity.authorizeHttpRequests(authorizeHttpRequests -> authorizeHttpRequests
                // 允许所有OPTIONS请求
                .requestMatchers(HttpMethod.OPTIONS, ALLOW_ALL).permitAll()
                // 允许直接访问授权登录接口
//                .requestMatchers(ALLOW_MATCHERS).permitAll()
                .requestMatchers(ALLOW_ALL).permitAll()
                // 其他所有接口必须有Authority信息，Authority在登录成功后的UserDetailsImpl对象中设置
                .requestMatchers(ALLOW_ALL).hasAnyAuthority(AUTHORITIES)
                // 允许任意请求被已登录用户访问，不检查Authority
                .anyRequest().authenticated()
        );
        // 配置认证、授权自定义异常处理
        httpSecurity.exceptionHandling(exceptionHandling -> {
            exceptionHandling.authenticationEntryPoint(authenticationEntryPoint);
            exceptionHandling.accessDeniedHandler(accessDeniedHandler);
        });

        // 把JWT安全校验过滤器添加到过滤器链中
        httpSecurity.addFilterBefore(jwtAuthFilter, UsernamePasswordAuthenticationFilter.class);
        return httpSecurity.build();
    }

    /**
     * 配置身份验证管理器
     */
    @Bean
    public AuthenticationManager authenticationManager(AuthenticationConfiguration authenticationConfiguration) throws Exception {
        return authenticationConfiguration.getAuthenticationManager();
    }

    public static void main(String[] args) {
        // 明文密码
        String password = "123456";
        // 创建加密对象
        BCryptPasswordEncoder bc = new BCryptPasswordEncoder();
        // 执行加密
        String enPwd = bc.encode(password);
        System.out.println(enPwd); // $2a$10$pn.vIl5oAEZHAXFpIeVzE.juqKILQFkbRWodM3/dOLviJYQYGOSQO

        // 使用明文密码和加密后的密码进行比对
        System.out.println(bc.matches(password, enPwd));
    }

}
